home *** CD-ROM | disk | FTP | other *** search
/ Scene 96 / Scene 96 International Edition (Zyklop Software) (Disc 2) (1997).iso / misc / coding / vgacodng / part09.txt < prev    next >
Text File  |  1996-12-15  |  12KB  |  280 lines

  1.  
  2.                              VGA-Kurs - Part #9
  3.  
  4. Hallo ihr VGA-Coder und die, die es werden wollen!
  5. Willkommen zur 9. Ausgabe von "T.C.P.'s Beginner's Guide To VGA-Coding"!
  6. Für diesen Teil habe ich mir einiges vorgenommen, nämlich die Prinzipien der
  7. 3D-Grafik. Das dies kein leichtes Thema ist, sollte man sich im Klaren darüber
  8. sein, daß man das Ganze als 3D-Neuling nicht gleich beim ersten Durchlesen
  9. kapieren kann. Es kostet einfach Zeit, die Zusammenhänge verstehen und diese
  10. dann in eigene Programme umsetzen zu können. Deshalb rate ich euch, tippt nicht
  11. einfach nur die Sources ab, sondern versucht herauszukriegen, was
  12. dahintersteckt, oder noch besser: schreibt alles selbst!
  13. Aber genug der Vorrede, lasset uns das heiße 3D-Eisen anpacken!
  14. Ich hoffe, jeder von euch kann sich einen dreidimensionalen Raum vorstellen.
  15. Zum Beispiel das Weltall. Nehmen wir einmal an, unsere Sonne wäre der
  16. Mittelpunkt des Weltalls. Ihre Position wird also durch die Koordinaten 0,0,0
  17. (X-, Y- und Z-Koordinate) definiert. Außerdem nehmen wir an, daß das Weltall
  18. 3 Achsen hat, die sich im Mittelpunkt der Sonne treffen. Die erste Achse (X)
  19. verläuft geradewegs horizontal von links nach rechts durchs komplette All und
  20. durch die Sonne. Die zweite (Y) verläuft vertikal von unten nach oben, und die
  21. dritte (Z) von uns (dem Betrachter) aus direkt durch die Sonne ins tiefe All.
  22. Als Einheiten für die Achsen nehmen wir Lichtjahre (weils zum Beispiel paßt).
  23. Jetzt denken wir uns irgendwo im Raum einen einsamen Stern. Um seine Position
  24. zu definieren, müssen wir seine Entfernung zum Nullpunkt (der Sonne) bestimmen.
  25. Angenommen, er liegt 7 Lichtjahre "rechts" von der Sonne, dann hat er die
  26. X-Koordinate 7. Liegt er zudem noch 2 Lichtjahre unterhalb der Sonne, ist
  27. seine Y-Koordinate -2, denn die Achse führt nach oben. Dann liegt er noch 5
  28. Lichtjahre weiter weg von uns als die Sonne, hat also die Z-Koordinate 5.
  29. Könnt ihr euch jetzt ungefähr vorstellen, in welcher Relation sich der Stern
  30. zur Sonne und unserem Auge (dem Sichtpunkt) befindet? Gut, dann ist euch das
  31. Wichtigste schon klar.
  32. Eine Umsetzung dieses Prinzips finden wir in den uns allen bekannten
  33. Sternenflugeffekten aus Demos oder Windows-Bildschirmschonern. Hier fliegt der
  34. Betrachter sozusagen durchs Weltall, wobei sich alle Sterne an ihm
  35. vorbeibewegen. Diesen Effekt werden wir heute besprechen.
  36. Zuallererst müssen wir die Positionen der Sterne definieren. Dazu bedienen wir
  37. uns eines Records:
  38.  
  39. type Star3D = record
  40.                 x,y,z : integer; { X-, Y- und Z-Koordinaten eines Sterns }
  41.               end;
  42.  
  43. Jetzt noch ein Array, in dem die Positionen aller Sterne gespeichert werden.
  44.  
  45. const StarNo = 1000; { 1000 Sterne }
  46. var Stars3D : array[1..StarNo] of Star3D; { Array mit allen Koordinaten }
  47.  
  48. Nun, mit 1000 Sternen haben wir ein sehr eingeschränktes Universum, aber wie
  49. bei einem normalen Scrolly auch, werden die Sterne einfach geloopt, d.h. die
  50. Sterne, an denen der Betrachter vorbeigezogen ist, werden ans Ende der Schleife
  51. wiederangehängt.
  52. Achtung, jetzt kommt der Trick: Eigentlich müßte sich im logischen Sinne der
  53. Betrachter fortbewegen, und die Sterne immer am selben Ort, also an denselben
  54. Koordinaten, verweilen. Aber der Einfachheit halber drehen wir den Spieß um:
  55. Wir definieren eine Variable ZAdd, die bei jedem Schleifendurchlauf vor der
  56. Darstellung der Sterne auf die Z-Koordinaten dieser addiert wird. Ist diese
  57. Variable negativ, bewegen sich die Sterne entgegengesetzt der Z-Achse, und damit
  58. auf den Betrachter zu, wodurch der Eindruck entsteht, er bewege sich vorwärts.
  59. Dabei definieren wir die Position des Betrachters der Einfachheit wegen als
  60. 0,0,0. Das einzige, was wir dabei beachten müssen ist, daß kein Stern die X-
  61. und Y-Koordinaten 0,0 hat, weil wir sonst mit ihm kollidieren würden.
  62. Alles klar? Nein? Dann das Ganze nochmal als Pseudocode.
  63.  
  64. begin
  65.   Zufällige_Positionen_für_Sterne_berechnen;
  66.   VGA_Modus_setzen;
  67.   Palette_setzen;
  68.   repeat
  69.     Sterne_weiterbewegen_durch_addieren_von_ZAdd;
  70.     Alte_Sternpositionen_sichern;
  71.     Sternenkoordinaten_umrechnen;
  72.     Auf_Vertical_Retrace_warten;
  73.     Alle_Sterne_löschen;
  74.     Sterne_darstellen;
  75.   until keypressed;
  76.   readkey;
  77.   Textmodus_setzen;
  78. end.
  79.  
  80. Aber jetzt zum Wichtigsten: der Darstellung. Wie berechne ich aus den X-,
  81. Y- und Z-Koordinaten eines Sterns (3D) die X- und Y-Koordinaten, die er auf dem
  82. Bildschirm (2D) haben muß?
  83. Eigentlich ganz einfach: Um X2D zu erhalten, teilen wir X3D durch Z3D, und für
  84. Y2D lautet die Formel Y3D durch Z3D. Warum das so ist, hat folgenden Grund.
  85. Stellen wir uns einmal eine richtig lange, gerade Straße vor, keine einzige
  86. Kurve, bis zum Horizont (der Traum für jeden Biker ;). Am Straßenrand stehen,
  87. wie hierzulande üblich, Straßenpfähle im Abstand von 50 Metern. Diese Pfähle
  88. stehen alle in einer Linie, haben also alle dieselbe X-Koordinate. Auch die
  89. Y-Koordinate ist bei allen gleich, denn die Straße verläuft völlig eben.
  90. Nur die Z-Koordinate unterscheidet sich jeweils um 50 Meter. Vergleichen wir
  91. nun zwei dieser Pfähle auf der linken Straßenseite, so fallen uns zwei
  92. Unterschiede auf. Erstens erscheint der hintere kleiner und zweitens ist er
  93. weiter rechts und weiter oben in unserem Blickfeld.
  94. Die erste Erkenntnis hilft uns bei unseren Sternen nicht viel, denn sie sind
  95. alle gleich groß (1 Pixel), aber aus der zweiten können wir ersehen, daß die
  96. Formel von vorhin richtig war. Je weiter ein Stern von uns entfernt ist, desto
  97. größer ist seine Z-Koordinate, teilen wir als X und Y durch Z, werden diese
  98. kleiner, wie auch in Wirklichkeit. Ist das nicht cool? 3D kann so einfach sein!
  99. So wird die Variable zum Speichern der umgerechneten Koordinaten definiert:
  100.  
  101. type Star2D = record
  102.                 x,y : integer;
  103.               end;
  104.      Star2DArray = array[1..StarNo] of Star2D;
  105. var Stars2D : Star2DArray;
  106.     Backup  : Star2DArray;
  107.  
  108. Wozu das Backup? Das hat Geschwindigkeitsgründe. Auf einem Computer unter
  109. Pentium ist nicht genug Zeit da, um zwischen dem Löschen der Sterne und dem
  110. Darstellen der Sterne die neuen Sternpositionen zu berechnen, ohne dabei mit dem
  111. Vertical Retrace in Konflikt zu kommen. Also werden die alten Sternpositionen
  112. in Backup gesichert, und vor der Darstellung die neuen berechnet.
  113. Die fertige Prozedur zum Umrechnen von 3D in 2D müßte so aussehen:
  114.  
  115. procedure Calc_2Dto3D;
  116. var n : integer;
  117. begin
  118.   for n := 1 to StarNo do begin
  119.     Stars2D[n].x := Stars3D[n].x * 128 div Stars3D[n].z + 160;
  120.     Stars2D[n].y := Stars3D[n].y * 128 div Stars3D[n].z + 100;
  121.   end;
  122. end;
  123.  
  124. Der Formel wurde noch ein '* 128' zugefügt, weil sonst die Werte für den
  125. Bildschirm zu klein sind, außerdem wird 160 bzw. 100 addiert, um die Mitte des
  126. Koordinatensystems in die Mitte des Screens zu verlagern.
  127. Eins fehlt uns jetzt noch zur perfekten (naja) 3D-Illusion. Die weiter
  128. entfernten Sterne sollten dunkler dargestellt werden als die, die dem Betrachter
  129. am nahsten sind. Das dürfte auch kein Problem sein, wenn wir die Sterne
  130. einfach je dunkler zeichnen, desto größer ihre Z-Koordinate ist.
  131. Dies ginge mit der Formel Farbe = Star3D.z div 10. Dies paßt allerdings
  132. nur, wenn man StarNo auf 1000 läßt, ansonsten muß man den Wert 10 etwas erhöhen.
  133. Das Ergebnis der Formel muß dann von 63 abgezogen werden, weil unsere Palette
  134. in 64 Graustufen von Schwarz nach Weiß verläuft.
  135. Außerdem werden nur Sterne dargestellt, deren Z-Koordinate < 500 ist, dies
  136. läßt den Effekt etwas realistischer wirken.
  137. Hier also die Prozeduren zum Darstellen, Löschen und Weiterbewegen der Sterne.
  138.  
  139. procedure DrawStars;
  140. var n : integer;
  141. begin
  142.   for n := 1 to StarNo do if (Stars2D[n].x > 0) and (Stars2D[n].x < 320)
  143.                              and (Stars2D[n].y > 0) and (Stars2D[n].y < 200)
  144.                              and (Stars3D[n].z < 500)
  145.                           then mem[$A000:Stars2D[n].y*320+Stars2D[n].x] :=
  146.                               63-Stars3D[n].z div 10;
  147. end;
  148.  
  149. procedure ClearStars;
  150. var n : integer;
  151. begin
  152.   for n := 1 to StarNo do if (Backup[n].x > 0) and (Backup[n].x < 320)
  153.                              and (Backup[n].y > 0) and (Backup[n].y < 200)
  154.                           then mem[$A000:Backup[n].y*320+Backup[n].x] := 0;
  155. end;
  156.  
  157. procedure MoveStars;
  158. var n : integer;
  159. begin
  160.   for n := 1 to StarNo do begin
  161.     inc(Stars3D[n].z,ZAdd);
  162.     if Stars3D[n].z < 1 then inc(Stars3D[n].z,StarNo);
  163.     if Stars3D[n].z > StarNo then dec(Stars3D[n].z,StarNo);
  164.   end;
  165. end;
  166.  
  167. Das war's eigentlich schon. Was uns noch fehlt, ist die Prozedur zum Erstellen
  168. des Sternen-Arrays. Das dürfte jetzt aber kein Problem mehr darstellen, oder?
  169.  
  170. procedure InitStars;
  171. var n : integer;
  172. begin
  173.   for n := 1 to StarNo do begin
  174.     repeat
  175.       Stars3D[n].x := random(640) - 320;
  176.       Stars3D[n].y := random(400) - 200;
  177.     until (Stars3D[n].x <> 0) and (Stars3D[n].y <> 0);
  178.     Stars3D[n].z := random(StarNo);
  179.   end;
  180. end;
  181.  
  182. Und schon haben wir alles zusammen was wir benötigen. Ach, ihr wollt auch noch
  183. das fertige Programm? Na gut, ich will euch ja nicht zu sehr fordern ;)
  184.  
  185. program Sternen_Effekt;
  186. uses crt;
  187.  
  188. const StarNo = 1000; { 1000 Sterne }
  189.  
  190. type Star3D = record
  191.                 x,y,z : integer;
  192.               end;
  193.      Star2D = record
  194.                 x,y : integer;
  195.               end;
  196.      Star2DArray = array[1..StarNo] of Star2D;
  197.  
  198. var Stars3D : array[1..StarNo] of Star3D;
  199.     Stars2D : Star2DArray;
  200.     Backup  : Star2DArray;
  201.     ZAdd,n  : integer;
  202.  
  203. { Hier Calc_3Dto2D, DrawStars, ClearStars und MoveStars einfügen }
  204.  
  205. procedure WaitRetrace;assembler;
  206. asm
  207.   mov     dx,3DAh
  208. @1:
  209.   in      al,dx
  210.   and     al,8
  211.   jz      @1
  212. @2:
  213.   in      al,dx
  214.   and     al,8
  215.   jz      @2
  216. end;
  217.  
  218. procedure SetPal(col,r,g,b:byte);assembler;
  219. asm
  220.   mov     dx,3C8h
  221.   mov     al,col
  222.   out     dx,al
  223.   inc     dx
  224.   mov     al,r
  225.   out     dx,al
  226.   mov     al,g
  227.   out     dx,al
  228.   mov     al,b
  229.   out     dx,al
  230. end;
  231.  
  232. begin
  233.   randomize;            { Zufallsgenerator anwerfen }
  234.   InitStars;            { Sternenarrays mit Werten füllen }
  235.   asm mov ax,13h; int 10h end; { VGA Modus setzen }
  236.   for n := 0 to 63 do SetPal(n,n,n,n); { Palette setzen }
  237.   ZAdd := -4;           { ZAdd initialisieren }
  238.   repeat
  239.     MoveStars;          { Sterne bewegen }
  240.     Backup := Stars2D;  { Alte Sternpositionen sichern }
  241.     Calc_2Dto3D;        { und neue berechnen }
  242.     WaitRetrace;        { Auf Retrace warten }
  243.     ClearStars;         { Alte Sterne löschen }
  244.     DrawStars;          { und neue zeichnen }
  245.   until keypressed;
  246.   readkey;
  247.   asm mov ax,3; int 10h end;
  248. end.
  249.  
  250. Die Variable ZAdd bestimmt die Geschwindigkeit der Sterne. Ist sie positiv,
  251. bewegen sie sich vom Betrachter weg.
  252.  
  253. Und wieder habt ihr eine (hoffentlich nicht zu schwere) Ausgabe meines
  254. VGA-Kurses überstanden. Für den nächsten Teil plane ich entweder die Fortsetzung
  255. des 3D-Kurses oder einen Kurs über SVGA-Coding (dafür gab es immerhin 5 Anfragen
  256. im PCH 4/96), inlusive High- und True-Color-Auflösungen. Bis denn!
  257.  
  258.  
  259.  
  260.  
  261.  
  262. [ This text copyright (c) 1995-96 Johannes Spohr. All rights reserved. ]
  263. [ Distributed exclusively through PC-Heimwerker, Verlag Thomas Eberle. ]
  264. [                                                                      ]
  265. [ No  part   of  this   document  may  be   reproduced,   transmitted, ]
  266. [ transcribed,  stored in a  retrieval system,  or translated into any ]
  267. [ human or computer language, in any form or by any means; electronic, ]
  268. [ mechanical,  magnetic,  optical,   chemical,  manual  or  otherwise, ]
  269. [ without the expressed written permission of the author.              ]
  270. [                                                                      ]
  271. [ The information  contained in this text  is believed  to be correct. ]
  272. [ The text is subject to change  without notice and does not represent ]
  273. [ a commitment on the part of the author.                              ]
  274. [ The author does not make a  warranty of any kind with regard to this ]
  275. [ material, including,  but not limited to,  the implied warranties of ]
  276. [ merchantability  and fitness  for a particular  purpose.  The author ]
  277. [ shall not be liable for errors contained herein or for incidental or ]
  278. [ consequential damages in connection with the furnishing, performance ]
  279. [ or use of this material.                                             ]
  280.